﻿using Microsoft.Extensions.Logging;
using Sage.Http.Options;

namespace Sage.Http.Handlers
{
    /// <summary>
    /// 实现熔断器模式的HTTP消息处理器
    /// </summary>
    /// <remarks>
    /// 初始化熔断器处理器
    /// </remarks>
    /// <param name="options">熔断器配置</param>
    /// <param name="logger">日志记录器</param>
    public class CircuitBreakerHandler(CircuitBreakerOptions options, ILogger logger) : DelegatingHandler
    {
        private readonly CircuitBreakerOptions _options = options ?? throw new ArgumentNullException(nameof(options));
        private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        private readonly Lock _stateLock = new();
        private CircuitState _currentState = CircuitState.Closed;
        private int _failureCount;
        private DateTime _lastStateChange = DateTime.UtcNow;
        private int _halfOpenSuccessCount;
        private int _halfOpenCurrentCount;

        /// <summary>
        /// 熔断器的状态
        /// </summary>
        public enum CircuitState
        {
            /// <summary>
            /// 闭合状态：正常处理请求
            /// </summary>
            Closed,

            /// <summary>
            /// 打开状态：快速失败所有请求
            /// </summary>
            Open,

            /// <summary>
            /// 半开状态：允许有限请求通过以测试服务是否恢复
            /// </summary>
            HalfOpen
        }

        /// <summary>
        /// 获取当前熔断器状态
        /// </summary>
        public CircuitState CurrentState
        {
            get
            {
                lock (_stateLock)
                {
                    return _currentState;
                }
            }
        }

        /// <summary>
        /// 拦截请求并应用熔断器逻辑
        /// </summary>
        /// <param name="request">HTTP请求消息</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>HTTP响应消息</returns>
        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            if (!_options.Enabled)
            {
                return await base.SendAsync(request, cancellationToken);
            }

            // 检查当前熔断器状态
            lock (_stateLock)
            {
                // 如果熔断器打开，快速失败
                if (_currentState == CircuitState.Open)
                {
                    TimeSpan sinceLastStateChange = DateTime.UtcNow - _lastStateChange;

                    // 检查是否达到恢复时间，如果是则切换到半开状态
                    if (sinceLastStateChange.TotalMilliseconds >= _options.RecoveryTimeMs)
                    {
                        TransitionToHalfOpen();
                    }
                    else
                    {
                        // 熔断器仍处于打开状态，快速失败
                        _logger.LogWarning(
                            "熔断器打开，快速失败请求。距离恢复还有 {TimeToRecovery}ms",
                            _options.RecoveryTimeMs - (int)sinceLastStateChange.TotalMilliseconds);

                        throw new HttpRequestException(
                            $"Circuit breaker is open. Service unavailable for {(int)sinceLastStateChange.TotalSeconds}s");
                    }
                }

                // 如果熔断器半开，控制通过的请求数
                if (_currentState == CircuitState.HalfOpen)
                {
                    if (_halfOpenCurrentCount >= _options.HalfOpenMaxRequests)
                    {
                        _logger.LogWarning(
                            "熔断器半开，已达到最大试探请求数 {MaxRequests}，快速失败",
                            _options.HalfOpenMaxRequests);

                        throw new HttpRequestException(
                            "Circuit breaker is half-open and has reached its request limit");
                    }

                    _halfOpenCurrentCount++;
                }
            }

            try
            {
                // 发送请求
                var response = await base.SendAsync(request, cancellationToken);

                // 检查响应是否表示故障
                bool isSuccess = IsSuccessResponse(response);

                lock (_stateLock)
                {
                    if (isSuccess)
                    {
                        // 成功处理
                        if (_currentState == CircuitState.HalfOpen)
                        {
                            _halfOpenSuccessCount++;

                            // 如果半开状态下的成功请求足够多，恢复到闭合状态
                            if (_halfOpenSuccessCount >= _options.HalfOpenMaxRequests)
                            {
                                TransitionToClosed();
                            }
                        }
                        else if (_currentState == CircuitState.Closed)
                        {
                            // 闭合状态下的成功请求，重置故障计数
                            _failureCount = 0;
                        }
                    }
                    else
                    {
                        // 失败处理
                        if (_currentState == CircuitState.HalfOpen)
                        {
                            // 半开状态下的失败请求，立即回到打开状态
                            TransitionToOpen();
                        }
                        else if (_currentState == CircuitState.Closed)
                        {
                            // 闭合状态下的失败请求，增加故障计数
                            _failureCount++;

                            _logger.LogWarning(
                                "请求失败，故障计数: {FailureCount}/{Threshold}",
                                _failureCount, _options.FailureThreshold);

                            // 如果故障计数达到阈值，切换到打开状态
                            if (_failureCount >= _options.FailureThreshold)
                            {
                                TransitionToOpen();
                            }
                        }
                    }
                }

                return response;
            }
            catch (Exception ex)
            {
                // 请求过程中出现异常
                lock (_stateLock)
                {
                    if (_currentState == CircuitState.HalfOpen)
                    {
                        // 半开状态下的异常，立即回到打开状态
                        _logger.LogWarning(ex, "半开状态下发生异常，熔断器将重新打开");
                        TransitionToOpen();
                    }
                    else if (_currentState == CircuitState.Closed)
                    {
                        // 闭合状态下的异常，增加故障计数
                        _failureCount++;

                        _logger.LogWarning(ex,
                            "请求异常，故障计数: {FailureCount}/{Threshold}",
                            _failureCount, _options.FailureThreshold);

                        // 如果故障计数达到阈值，切换到打开状态
                        if (_failureCount >= _options.FailureThreshold)
                        {
                            TransitionToOpen();
                        }
                    }
                }

                throw;

            }
        }

        /// <summary>
        /// 判断响应是否表示成功
        /// </summary>
        /// <param name="response">HTTP响应消息</param>
        /// <returns>响应是否表示成功</returns>
        private bool IsSuccessResponse(HttpResponseMessage response)
        {
            int statusCode = (int)response.StatusCode;

            // 检查用户是否明确配置了TripStatusCodes
            if (_options.TripStatusCodes != null && _options.TripStatusCodes.Length > 0)
            {
                // 有明确配置，只有在列表中的状态码才被视为失败
                return !_options.TripStatusCodes.Contains(statusCode);
            }

            // 没有明确配置，使用默认行为：所有4xx和5xx都视为失败
            return statusCode < 400;
        }

        /// <summary>
        /// 切换到闭合状态
        /// </summary>
        private void TransitionToClosed()
        {
            _currentState = CircuitState.Closed;
            _failureCount = 0;
            _lastStateChange = DateTime.UtcNow;

            _logger.LogInformation("熔断器已关闭，恢复正常服务");
        }

        /// <summary>
        /// 切换到打开状态
        /// </summary>
        private void TransitionToOpen()
        {
            _currentState = CircuitState.Open;
            _lastStateChange = DateTime.UtcNow;

            _logger.LogWarning(
                "熔断器已打开，服务将在 {RecoveryTimeMs}ms 后重试",
                _options.RecoveryTimeMs);
        }

        /// <summary>
        /// 切换到半开状态
        /// </summary>
        private void TransitionToHalfOpen()
        {
            _currentState = CircuitState.HalfOpen;
            _halfOpenSuccessCount = 0;
            _halfOpenCurrentCount = 0;
            _lastStateChange = DateTime.UtcNow;

            _logger.LogInformation(
                "熔断器处于半开状态，允许最多 {MaxRequests} 个试探请求",
                _options.HalfOpenMaxRequests);
        }
    }
}
